package github.sf.fw.tools.spi;

import github.sf.fw.utils.RefUtils;

import java.util.Map;
import java.util.ServiceLoader;
import java.util.concurrent.ConcurrentHashMap;

/**
 * 类工厂,对每个类型的ClassFactory，尽量只初始化一次
 *
 * @author zhoup
 */
public class ClassFactory<InterfaceClass> {
    private static int loadObjectCounter;
    private static int loadClassCounter;
    private final Map<String, InterfaceClass> componentCache = new ConcurrentHashMap<>();
    private final Map<String, Class<? extends InterfaceClass>> componentClassCache = new ConcurrentHashMap<>();
    private final ClassNameGenerator classNameGenerator;

    private final Object lock = new Object();

    public ClassFactory(ClassNameGenerator classNameGenerator) {
        this.classNameGenerator = classNameGenerator;
    }

    public ClassFactory(ClassNameGenerator classNameGenerator, Class<InterfaceClass> interfaceClassClass) {
        this.classNameGenerator = classNameGenerator;
        loadServiceClasses(Thread.currentThread().getContextClassLoader(), interfaceClassClass);
    }

    public ClassFactory(ClassNameGenerator classNameGenerator, Class<InterfaceClass> interfaceClassClass, ClassLoader classLoader) {
        this.classNameGenerator = classNameGenerator;
        loadServiceClasses(classLoader, interfaceClassClass);
    }

    @Deprecated
    @SuppressWarnings(value = {"unchecked"})
    public synchronized void loadServiceObjects(ClassLoader classLoader, Class<InterfaceClass> clazz) {
        cleanCache();
        ServiceLoader<InterfaceClass> funcComponents = ServiceLoader.load(clazz, classLoader);
        loadObjectCounter++;
        loadClassCounter++;
        for (InterfaceClass component : funcComponents) {
            String componentName = classNameGenerator.getName(component.getClass());
            componentClassCache.put(componentName, (Class<? extends InterfaceClass>) component.getClass());
            componentCache.put(componentName, component);
        }
    }

    public synchronized void loadServiceClasses(ClassLoader classLoader, Class<InterfaceClass> clazz) {
        cleanCache();
        //使用不会初始化类的服务加载器
        NoInitClassServiceLoader<InterfaceClass> funcComponents = NoInitClassServiceLoader.load(clazz, classLoader);
        loadClassCounter++;
        for (Class<? extends InterfaceClass> component : funcComponents) {
            String componentName = classNameGenerator.getName(component);
            componentClassCache.put(componentName, component);
        }

    }


    public InterfaceClass getNewComponent(String componentName) {
        Class<? extends InterfaceClass> clazz = componentClassCache.get(componentName);
        if (clazz != null) {
            return RefUtils.newInstance(clazz);
        }
        throw new UnsupportedOperationException(String.format("not support %1$s type agent", componentName));
    }

    /**
     * 用处不大,不推荐使用
     *
     * @param clazz 具体的实现类
     * @return 具体的实现类实例
     */
    public InterfaceClass getNewComponent(Class<InterfaceClass> clazz) {
        String name = classNameGenerator.getName(clazz);
        return getNewComponent(name);
    }

    public InterfaceClass getComponentWithCache(String componentName) {
        InterfaceClass component = componentCache.get(componentName);
        if (component == null) {
            synchronized (lock) {
                //这里再次判断是防止多线程读取时，重复创建agent
                component = componentCache.get(componentName);
                if (component == null) {
                    Class<? extends InterfaceClass> clazz = componentClassCache.get(componentName);
                    component = RefUtils.newInstance(clazz);
                    String keyName = classNameGenerator.getName(clazz);
                    componentCache.put(keyName, component);
                }
            }
        }
        return component;
    }

    /**
     * 用处不大，不推荐使用
     *
     * @param clazz 具体的实现类类型
     * @return 具体的实现类实例
     */
    public InterfaceClass getComponentWithCache(Class<InterfaceClass> clazz) {
        String name = classNameGenerator.getName(clazz);
        return getComponentWithCache(name);
    }

    public Class<? extends InterfaceClass> getComponentClass(String componentName) {
        return componentClassCache.get(componentName);
    }

    public int getLoadObjectCounter() {
        return loadObjectCounter;
    }

    public int getLoadClassCounter() {
        return loadClassCounter;
    }

    protected void cleanCache() {
        componentCache.clear();
        componentClassCache.clear();
    }

    public ClassNameGenerator getClassNameGenerator() {
        return classNameGenerator;
    }

    public boolean isEmpty() {
        return componentCache.isEmpty() && componentClassCache.isEmpty();
    }
}
